﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
using ActivateYourGlutes.Controllers;
using ActivateYourGlutes.Infrastructure.MembershipServices;
using ActivateYourGlutes.Model;
using ActivateYourGlutes.Model.Contracts;
using ActivateYourGlutes.Model.Providers;
using ActivateYourGlutes.Payloads.Account;
using ActivateYourGlutes.Tests.Unit.Mocks;
using ActivateYourGlutes.Helpers;
using NUnit.Framework;
using Rhino.Mocks;

namespace ActivateYourGlutes.Tests.Unit.Controllers
{
    [TestFixture]
    public class Test_AccountController : ControllerTestBase
    {
        private const string AccountEmail = "testuser@somedomain.com";
        private const int MinPasswordLength = 6;
        private AccountController _controller;
        private IMembershipService _membershipService;
        private IFormsAuthentication _formsAuth;
        private IAccountProvider _accountProvider;
        private ILookupProvider _lookupProvider;
        private IBodyProvider _bodyProvider;

        [SetUp]
        public override void Init()
        {
            base.Init();
            _mockAccount.SecondaryMeasurementType = null;
        }

        [TearDown]
        public override void TearDown()
        {
            base.TearDown();
            _controller = null;
            _membershipService = null;
            _lookupProvider = null;
            _formsAuth = null;
            _accountProvider = null;
            _lookupProvider = null;
            _bodyProvider = null;
        }

        #region Controller and dependency creation

        private void CreateStubs()
        {
            _formsAuth = MockRepository.GenerateStub<IFormsAuthentication>();
            _membershipService = MockRepository.GenerateStub<IMembershipService>();
            _accountProvider = MockRepository.GenerateStub<IAccountProvider>();
            _bodyProvider = MockRepository.GenerateStub<IBodyProvider>();
            _lookupProvider = MockRepository.GenerateStub<ILookupProvider>();
        }

        private void CreateController()
        {
            MockHttpContext controllerContext = new MockHttpContext();
            _controller = new AccountController(_accountProvider, _bodyProvider, _lookupProvider, _formsAuth, _membershipService);
            _controller.ControllerContext = new ControllerContext(controllerContext, new RouteData(), _controller);
            _controller.LocaleSettings = _localeSettings;
            _controller.Url = new UrlHelper(new RequestContext(controllerContext, new RouteData()));
        }

        private void InitialiseControllerWithMockMembershipService()
        {
            CreateStubs();
            _membershipService = MockRepository.GenerateMock<IMembershipService>();
            _membershipService.Stub(ms => ms.MinPasswordLength).Return(MinPasswordLength);
            CreateController();
        }

        private void InitialiseControllerWithMockLookupProvider()
        {
            CreateStubs();
            _lookupProvider = MockRepository.GenerateMock<ILookupProvider>();
            CreateController();
        }

        private void InitialiseControllerWithMockAccountProvider()
        {
            CreateStubs();
            _accountProvider = MockRepository.GenerateMock<IAccountProvider>();
            _accountProvider.Stub(ap => ap.CurrentAccount(null)).Return(_mockAccount).IgnoreArguments();
            CreateController();
        }

        private void InitialiseControllerWithMockFormsAuthenticator()
        {
            CreateStubs();
            _formsAuth = MockRepository.GenerateMock<IFormsAuthentication>();
            CreateController();
        }

        private void SetupMembershipServiceAccountStub()
        {
            _membershipService.Stub(ms => ms.Email(null)).Return(AccountEmail).IgnoreArguments();
        }

        #endregion

        #region Change password unit tests

        [Test]
        public void Test_ChangePassword_Get_ReturnsView()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();

            // Act
            ViewResult result = (ViewResult)_controller.ChangePassword();

            // Assert
            Assert.AreEqual(MinPasswordLength, result.ViewData["PasswordLength"]);
        }

        [Test]
        public void Test_ChangePassword_Post_RedirectsOnSuccess()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();
            _membershipService.Expect(ms => ms.ChangePassword(null, null, null)).Return(true).IgnoreArguments();

            // Act
            RedirectToRouteResult result = (RedirectToRouteResult)_controller.ChangePassword("oldPass", "newPass", "newPass");

            // Assert
            Assert.AreEqual("ChangePasswordSuccess", result.RouteValues["action"]);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_ChangePassword_Post_ReturnsViewIfCurrentPasswordNotSpecified()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();
            NameValueCollection errors = new NameValueCollection();
            errors.Add("currentPassword", "You must specify a current password.");
            _accountProvider.Stub(ap => ap.ValidateChangePassword(null, null, null, _membershipService))
                .IgnoreArguments().Throw(new ValidationException(errors));
            _membershipService.AssertWasNotCalled(ms => ms.ChangePassword(Arg<string>.Is.Anything,
                                                                          Arg<string>.Is.Anything,
                                                                          Arg<string>.Is.Anything));

            // Act
            ViewResult result = (ViewResult)_controller.ChangePassword("", "newPassword", "newPassword");

            // Assert
            Assert.AreEqual(MinPasswordLength, result.ViewData["PasswordLength"]);
            Assert.AreEqual("You must specify a current password.", result.ViewData.ModelState["currentPassword"].Errors[0].ErrorMessage);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_ChangePassword_Post_ReturnsViewIfNewPasswordDoesNotMatchConfirmPassword()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();
            NameValueCollection errors = new NameValueCollection();
            errors.Add("_FORM", "The new password and confirmation password do not match.");
            _accountProvider.Stub(ap => ap.ValidateChangePassword(null, null, null, _membershipService))
                .IgnoreArguments().Throw(new ValidationException(errors));
            _membershipService.AssertWasNotCalled(ms => ms.ChangePassword(Arg<string>.Is.Anything,
                                                                          Arg<string>.Is.Anything,
                                                                          Arg<string>.Is.Anything));

            // Act
            ViewResult result = (ViewResult)_controller.ChangePassword("currentPassword", "newPassword", "otherPassword");

            // Assert
            Assert.AreEqual(MinPasswordLength, result.ViewData["PasswordLength"]);
            Assert.AreEqual("The new password and confirmation password do not match.", result.ViewData.ModelState["_FORM"].Errors[0].ErrorMessage);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_ChangePassword_Post_MembershipServiceThrowsException()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();
            _membershipService.Expect(ms => ms.ChangePassword(null, null, null)).Throw(new MockException()).IgnoreArguments();

            // Act
            ViewResult result = (ViewResult)_controller.ChangePassword("oldPass", "newPass", "newPass");

            // Assert
            Assert.AreEqual(MinPasswordLength, result.ViewData["PasswordLength"]);
            Assert.AreEqual("The current password is incorrect or the new password is invalid.", result.ViewData.ModelState["_FORM"].Errors[0].ErrorMessage);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_ChangePassword_Post_MembershipServiceReturnsFalse()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();
            _membershipService.Expect(ms => ms.ChangePassword(null, null, null)).Return(false).IgnoreArguments();

            // Act
            ViewResult result = (ViewResult)_controller.ChangePassword("oldPass", "newPass", "newPass");

            // Assert
            Assert.AreEqual(MinPasswordLength, result.ViewData["PasswordLength"]);
            Assert.AreEqual("The current password is incorrect or the new password is invalid.", result.ViewData.ModelState["_FORM"].Errors[0].ErrorMessage);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_ChangePassword_Post_NewPasswordTooShort()
        {
            // Arrange
            string expectedError = String.Format(CultureInfo.CurrentCulture,
                                                 "You must specify a new password of {0} or more characters.",
                                                 MinPasswordLength);
            InitialiseControllerWithMockMembershipService();
            NameValueCollection errors = new NameValueCollection();
            errors.Add("newPassword", expectedError);
            _accountProvider.Stub(ap => ap.ValidateChangePassword(null, null, null, _membershipService))
                .IgnoreArguments().Throw(new ValidationException(errors));
            _membershipService.AssertWasNotCalled(ms => ms.ChangePassword(Arg<string>.Is.Anything,
                                                                          Arg<string>.Is.Anything,
                                                                          Arg<string>.Is.Anything));

            // Act
            ViewResult result = (ViewResult)_controller.ChangePassword("oldPass", "short", "short");

            // Assert
            Assert.AreEqual(MinPasswordLength, result.ViewData["PasswordLength"]);
            Assert.AreEqual(expectedError, result.ViewData.ModelState["newPassword"].Errors[0].ErrorMessage);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_ChangePassword_Get_Success()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();

            // Act
            ViewResult result = (ViewResult) _controller.ChangePasswordSuccess();

            // Assert
            // Nothing to do. Test included for completeness.
        }

        #endregion

        #region Registration unit tests

        private IEnumerable<UnitOfWeight> GetMockUnitOfWeightList()
        {
            return new List<UnitOfWeight>()
                       {
                           _kilogrammes,
                           new UnitOfWeight() {Description = "lbs"}
                       };
        }

        private IEnumerable<UnitOfLength> GetMockUnitOfLengthList()
        {
            return new List<UnitOfLength>()
                       {
                           _centimeters,
                           new UnitOfLength() {Description = "inches"}
                       };
        }

        private void SetupLookupStubs()
        {
            _lookupProvider.Stub(lp => lp.UnitsOfLength).Return(GetMockUnitOfLengthList());
            _lookupProvider.Stub(lp => lp.UnitsOfWeight).Return(GetMockUnitOfWeightList());
            _bodyProvider.Stub(bp => bp.MeasurementTypes).Return(_measurementTypes);
        }

        private void Assert_RegisterViewData(ViewResult result)
        {
            var payload = (RegisterPayload)result.ViewData.Model;
            Assert.AreEqual("cm", payload.UnitsOfLength.First().Text);
            Assert.AreEqual(2, payload.UnitsOfLength.Count());
            Assert.AreEqual("kg", payload.UnitsOfWeight.First().Text);
            Assert.AreEqual(2, payload.UnitsOfWeight.Count());
            Assert.AreEqual(MinPasswordLength, payload.MinimumPasswordLength);
        }

        [Test]
        public void Test_Register_Get_ReturnsView()
        {
            // Arrange
            IEnumerable<UnitOfLength> mockUnitOfLengthList = GetMockUnitOfLengthList();
            IEnumerable<UnitOfWeight> mockUnitOfWeightList = GetMockUnitOfWeightList();

            InitialiseControllerWithMockLookupProvider();
            _membershipService.Stub(ms => ms.MinPasswordLength).Return(MinPasswordLength);
            _lookupProvider.Expect(lp => lp.UnitsOfLength).Return(mockUnitOfLengthList);
            _lookupProvider.Expect(lp => lp.UnitsOfWeight).Return(mockUnitOfWeightList);

            // Act
            ViewResult result = (ViewResult) _controller.Register();

            // Assert
            Assert_RegisterViewData(result);
            _lookupProvider.VerifyAllExpectations();
        }

        [Test]
        public void Test_Register_Post_RedirectsToHomePage()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();
            _membershipService.Expect(ms => ms.CreateUser(null, null, null)).IgnoreArguments().Return(
                MembershipCreateStatus.Success);
            SetupLookupStubs();
            DateTime dateOfBirth18 = DateTime.Now.AddYears(-18);

            // Act
            RedirectResult result =
                (RedirectResult)
                _controller.Register(dateOfBirth18.ToString("MM-dd-yyyy"), "month-day-year", "testuser", "testemail@mailbox.com", "password",
                                     "password", 0, 0, true);


            // Assert
            Assert.AreEqual(MockHttpContext.Url, result.Url);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_Register_Post_InvalidDobStringFormat()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();
            NameValueCollection errors = new NameValueCollection();
            errors.Add("dateOfBirth", "Date of birth is required and must use the format mm-dd-yyyy");
            _accountProvider.Stub(ap => ap.ValidateRegistration(null, null, null, null, false, null, _membershipService))
                .IgnoreArguments().Throw(new ValidationException(errors));
            _membershipService.AssertWasNotCalled(ms => ms.CreateUser(Arg<string>.Is.Anything, Arg<string>.Is.Anything, Arg<string>.Is.Anything));
            SetupLookupStubs();

            // Act
            ViewResult result =
                (ViewResult)
                _controller.Register("corruptdob", "month-day-year", "testuser", "testemail@mailbox.com", "password",
                                     "password", 0, 0, true);

            // Assert
            Assert_RegisterViewData(result);
            Assert.AreEqual("Date of birth is required and must use the format mm-dd-yyyy", result.ViewData.ModelState["dateOfBirth"].Errors[0].ErrorMessage);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_Register_Post_InvalidDobUnder18()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();
            NameValueCollection errors = new NameValueCollection();
            errors.Add("dateOfBirth", "You must be at least 18 years old to use this site.");
            _accountProvider.Stub(ap => ap.ValidateRegistration(null, null, null, null, false, null, _membershipService))
                .IgnoreArguments().Throw(new ValidationException(errors));
            _membershipService.AssertWasNotCalled(ms => ms.CreateUser(Arg<string>.Is.Anything, Arg<string>.Is.Anything, Arg<string>.Is.Anything));
            SetupLookupStubs();
            DateTime dateOfBirth17Years364Days = DateTime.Now.AddYears(-17).AddDays(-364);

            // Act
            ViewResult result =
                (ViewResult)
                _controller.Register(dateOfBirth17Years364Days.ToString("MM-dd-yyyy"), "month-day-year", "testuser", "testemail@mailbox.com", "password",
                                     "password", 0, 0, true);

            // Assert
            Assert_RegisterViewData(result);
            Assert.AreEqual("You must be at least 18 years old to use this site.", result.ViewData.ModelState["dateOfBirth"].Errors[0].ErrorMessage);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_Register_Post_EmptyUsername()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();
            NameValueCollection errors = new NameValueCollection();
            errors.Add("username", "You must specify a username.");
            _accountProvider.Stub(ap => ap.ValidateRegistration(null, null, null, null, false, null, _membershipService))
                .IgnoreArguments().Throw(new ValidationException(errors));
            _membershipService.AssertWasNotCalled(ms => ms.CreateUser(Arg<string>.Is.Anything, Arg<string>.Is.Anything, Arg<string>.Is.Anything));
            SetupLookupStubs();
            DateTime dateOfBirth18Years = DateTime.Now.AddYears(-18);

            // Act
            ViewResult result =
                (ViewResult)
                _controller.Register(dateOfBirth18Years.ToString("MM-dd-yyyy"), "month-day-year", "", "testemail@mailbox.com", "password",
                                     "password", 0, 0, true);

            // Assert
            Assert_RegisterViewData(result);
            Assert.AreEqual("You must specify a username.", result.ViewData.ModelState["username"].Errors[0].ErrorMessage);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_Register_Post_NullUsername()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();
            NameValueCollection errors = new NameValueCollection();
            errors.Add("username", "You must specify a username.");
            _accountProvider.Stub(ap => ap.ValidateRegistration(null, null, null, null, false, null, _membershipService))
                .IgnoreArguments().Throw(new ValidationException(errors));
            _membershipService.AssertWasNotCalled(ms => ms.CreateUser(Arg<string>.Is.Anything, Arg<string>.Is.Anything, Arg<string>.Is.Anything));
            SetupLookupStubs();
            DateTime dateOfBirth18Years = DateTime.Now.AddYears(-18);

            // Act
            ViewResult result =
                (ViewResult)
                _controller.Register(dateOfBirth18Years.ToString("MM-dd-yyyy"), "month-day-year", null, "testemail@mailbox.com", "password",
                                     "password", 0, 0, true);

            // Assert
            Assert_RegisterViewData(result);
            Assert.AreEqual("You must specify a username.", result.ViewData.ModelState["username"].Errors[0].ErrorMessage);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_Register_Post_SpaceFilledUsername()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();
            NameValueCollection errors = new NameValueCollection();
            errors.Add("username", "You must specify a username.");
            _accountProvider.Stub(ap => ap.ValidateRegistration(null, null, null, null, false, null, _membershipService))
                .IgnoreArguments().Throw(new ValidationException(errors));
            _membershipService.AssertWasNotCalled(ms => ms.CreateUser(Arg<string>.Is.Anything, Arg<string>.Is.Anything, Arg<string>.Is.Anything));
            SetupLookupStubs();
            DateTime dateOfBirth18Years = DateTime.Now.AddYears(-18);

            // Act
            ViewResult result =
                (ViewResult)
                _controller.Register(dateOfBirth18Years.ToString("MM-dd-yyyy"), "month-day-year", "    ", "testemail@mailbox.com", "password",
                                     "password", 0, 0, true);

            // Assert
            Assert_RegisterViewData(result);
            Assert.AreEqual("You must specify a username.", result.ViewData.ModelState["username"].Errors[0].ErrorMessage);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_Register_Post_EmptyEmail()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();
            NameValueCollection errors = new NameValueCollection();
            errors.Add("email", "You must specify an email address.");
            _accountProvider.Stub(ap => ap.ValidateRegistration(null, null, null, null, false, null, _membershipService))
                .IgnoreArguments().Throw(new ValidationException(errors));
            _membershipService.AssertWasNotCalled(ms => ms.CreateUser(Arg<string>.Is.Anything, Arg<string>.Is.Anything, Arg<string>.Is.Anything));
            SetupLookupStubs();
            DateTime dateOfBirth18Years = DateTime.Now.AddYears(-18);

            // Act
            ViewResult result =
                (ViewResult)
                _controller.Register(dateOfBirth18Years.ToString("MM-dd-yyyy"), "month-day-year", "testuser", "", "password",
                                     "password", 0, 0, true);

            // Assert
            Assert_RegisterViewData(result);
            Assert.AreEqual("You must specify an email address.", result.ViewData.ModelState["email"].Errors[0].ErrorMessage);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_Register_Post_NullEmail()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();
            NameValueCollection errors = new NameValueCollection();
            errors.Add("email", "You must specify an email address.");
            _accountProvider.Stub(ap => ap.ValidateRegistration(null, null, null, null, false, null, _membershipService))
                .IgnoreArguments().Throw(new ValidationException(errors));
            _membershipService.AssertWasNotCalled(ms => ms.CreateUser(Arg<string>.Is.Anything, Arg<string>.Is.Anything, Arg<string>.Is.Anything));
            SetupLookupStubs();
            DateTime dateOfBirth18Years = DateTime.Now.AddYears(-18);

            // Act
            ViewResult result =
                (ViewResult)
                _controller.Register(dateOfBirth18Years.ToString("MM-dd-yyyy"), "month-day-year", "testuser", null, "password",
                                     "password", 0, 0, true);

            // Assert
            Assert_RegisterViewData(result);
            Assert.AreEqual("You must specify an email address.", result.ViewData.ModelState["email"].Errors[0].ErrorMessage);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_Register_Post_InvalidEmail()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();
            NameValueCollection errors = new NameValueCollection();
            errors.Add("email", "You must specify a valid email address.");
            _accountProvider.Stub(ap => ap.ValidateRegistration(null, null, null, null, false, null, _membershipService))
                .IgnoreArguments().Throw(new ValidationException(errors));
            _membershipService.AssertWasNotCalled(ms => ms.CreateUser(Arg<string>.Is.Anything, Arg<string>.Is.Anything, Arg<string>.Is.Anything));
            SetupLookupStubs();
            DateTime dateOfBirth18Years = DateTime.Now.AddYears(-18);

            // Act
            ViewResult result =
                (ViewResult)
                _controller.Register(dateOfBirth18Years.ToString("MM-dd-yyyy"), "month-day-year", "testuser", "james@missingdomain", "password",
                                     "password", 0, 0, true);

            // Assert
            Assert_RegisterViewData(result);
            Assert.AreEqual("You must specify a valid email address.", result.ViewData.ModelState["email"].Errors[0].ErrorMessage);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_Register_Post_PasswordMismatch()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();
            NameValueCollection errors = new NameValueCollection();
            errors.Add("_FORM", "The new password and confirmation password do not match.");
            _accountProvider.Stub(ap => ap.ValidateRegistration(null, null, null, null, false, null, _membershipService))
                .IgnoreArguments().Throw(new ValidationException(errors));
            _membershipService.AssertWasNotCalled(ms => ms.CreateUser(Arg<string>.Is.Anything, Arg<string>.Is.Anything, Arg<string>.Is.Anything));
            SetupLookupStubs();
            DateTime dateOfBirth18Years = DateTime.Now.AddYears(-18);

            // Act
            ViewResult result =
                (ViewResult)
                _controller.Register(dateOfBirth18Years.ToString("MM-dd-yyyy"), "month-day-year", "testuser", "email@email.com", "password_one",
                                     "password_two", 0, 0, true);

            // Assert
            Assert_RegisterViewData(result);
            Assert.AreEqual("The new password and confirmation password do not match.", result.ViewData.ModelState["_FORM"].Errors[0].ErrorMessage);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_Register_Post_PasswordTooShort()
        {
            // Arrange
            string message = String.Format(CultureInfo.CurrentCulture,
                                        "You must specify a password of {0} or more characters.",
                                        MinPasswordLength);
            InitialiseControllerWithMockMembershipService();
            NameValueCollection errors = new NameValueCollection();
            errors.Add("password", message);
            _accountProvider.Stub(ap => ap.ValidateRegistration(null, null, null, null, false, null, _membershipService))
                .IgnoreArguments().Throw(new ValidationException(errors));
            _membershipService.AssertWasNotCalled(ms => ms.CreateUser(Arg<string>.Is.Anything, Arg<string>.Is.Anything, Arg<string>.Is.Anything));
            SetupLookupStubs();
            DateTime dateOfBirth18Years = DateTime.Now.AddYears(-18);

            // Act
            ViewResult result =
                (ViewResult)
                _controller.Register(dateOfBirth18Years.ToString("MM-dd-yyyy"), "month-day-year", "testuser", "email@email.com", "pass",
                                     "pass", 0, 0, true);

            // Assert
            Assert_RegisterViewData(result);
            Assert.AreEqual(message, result.ViewData.ModelState["password"].Errors[0].ErrorMessage);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_Register_Post_TermsNotAccepted()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();
            NameValueCollection errors = new NameValueCollection();
            errors.Add("acceptTerms", "You must agree to the terms of service and privacy policy.");
            _accountProvider.Stub(ap => ap.ValidateRegistration(null, null, null, null, false, null, _membershipService))
                .IgnoreArguments().Throw(new ValidationException(errors));
            _membershipService.AssertWasNotCalled(ms => ms.CreateUser(Arg<string>.Is.Anything, Arg<string>.Is.Anything, Arg<string>.Is.Anything));
            SetupLookupStubs();
            DateTime dateOfBirth18Years = DateTime.Now.AddYears(-18);

            // Act
            ViewResult result =
                (ViewResult)
                _controller.Register(dateOfBirth18Years.ToString("MM-dd-yyyy"), "month-day-year", "testuser", "email@email.com", "password",
                                     "password", 0, 0, false);

            // Assert
            Assert_RegisterViewData(result);
            Assert.AreEqual("You must agree to the terms of service and privacy policy.", result.ViewData.ModelState["acceptTerms"].Errors[0].ErrorMessage);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_Register_Post_MembershipServiceReturnsDuplicateUserName()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();
            _membershipService.Expect(ms => ms.CreateUser(null, null, null)).IgnoreArguments().Return(
                MembershipCreateStatus.DuplicateUserName);
            SetupLookupStubs();
            DateTime dateOfBirth18 = DateTime.Now.AddYears(-18);

            // Act
            ViewResult result =
                (ViewResult)
                _controller.Register(dateOfBirth18.ToString("MM-dd-yyyy"), "month-day-year", "testuser", "testemail@mailbox.com", "password",
                                     "password", 0, 0, true);


            // Assert
            Assert_RegisterViewData(result);
            Assert.AreEqual("Username already exists. Please enter a different user name.", result.ViewData.ModelState["_FORM"].Errors[0].ErrorMessage);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_Register_Post_MembershipServiceReturnsInvalidPassword()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();
            _membershipService.Expect(ms => ms.CreateUser(null, null, null)).IgnoreArguments().Return(
                MembershipCreateStatus.InvalidPassword);
            SetupLookupStubs();
            DateTime dateOfBirth18 = DateTime.Now.AddYears(-18);

            // Act
            ViewResult result =
                (ViewResult)
                _controller.Register(dateOfBirth18.ToString("MM-dd-yyyy"), "month-day-year", "testuser", "testemail@mailbox.com", "password",
                                     "password", 0, 0, true);


            // Assert
            Assert_RegisterViewData(result);
            Assert.AreEqual("The password provided is invalid. Please enter a valid password value.", result.ViewData.ModelState["_FORM"].Errors[0].ErrorMessage);
            _membershipService.VerifyAllExpectations();
        }

        [Test]
        public void Test_Register_Post_MembershipServiceReturnsCatchAllError()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();
            _membershipService.Expect(ms => ms.CreateUser(null, null, null)).IgnoreArguments().Return(
                MembershipCreateStatus.DuplicateProviderUserKey);
            SetupLookupStubs();
            DateTime dateOfBirth18 = DateTime.Now.AddYears(-18);

            // Act
            ViewResult result =
                (ViewResult)
                _controller.Register(dateOfBirth18.ToString("MM-dd-yyyy"), "month-day-year", "testuser", "testemail@mailbox.com", "password",
                                     "password", 0, 0, true);


            // Assert
            Assert_RegisterViewData(result);
            Assert.AreEqual("An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator.", result.ViewData.ModelState["_FORM"].Errors[0].ErrorMessage);
            _membershipService.VerifyAllExpectations();
        }

        #endregion

        #region Tests for the index (account details)

        private void Assert_IndexViewData(ViewResult result)
        {
            var payload = (IndexPayload) result.ViewData.Model;
            // Lookups
            Assert.AreEqual("cm", payload.UnitsOfLength.First().Text);
            Assert.AreEqual(2, payload.UnitsOfLength.Count());
            Assert.AreEqual("kg", payload.UnitsOfWeight.First().Text);
            Assert.AreEqual(2, payload.UnitsOfWeight.Count());

            // The rest
            Assert.AreEqual(AccountEmail, payload.Email);
            Assert.AreEqual(_mockAccount, payload.Account);
        }

        [Test]
        public void Test_Index_Get_ReturnsView_SingleCheckedKeyIndicator()
        {
            // Arrange
            InitialiseControllerWithMockAccountProvider();
            SetupLookupStubs();
            SetupMembershipServiceAccountStub();
            
            // Act
            ViewResult result = (ViewResult)_controller.Index();

            // Assert
            var payload = (IndexPayload) result.ViewData.Model;
            Assert_IndexViewData(result);
            Assert.IsNull(payload.Success);
            Assert.IsNull(payload.HorizontalMeasurementTypesPayload.Error);
            Assert.AreEqual(_measurementTypes, payload.HorizontalMeasurementTypesPayload.MeasurementTypes);
            Assert.AreEqual(1, payload.HorizontalMeasurementTypesPayload.SelectedTypes.Count());
            Assert.AreEqual(_weight, payload.HorizontalMeasurementTypesPayload.SelectedTypes.First());
        }

        [Test]
        public void Test_Index_Get_ReturnsView_DoubleCheckedKeyIndicator()
        {
            // Arrange
            InitialiseControllerWithMockAccountProvider();
            SetupLookupStubs();
            SetupMembershipServiceAccountStub();
            _mockAccount.SecondaryMeasurementType = _bodyFat;

            // Act
            ViewResult result = (ViewResult)_controller.Index();

            // Assert
            Assert_IndexViewData(result);
            var payload = (IndexPayload)result.ViewData.Model;
            Assert.IsNull(payload.Success);
            Assert.IsNull(payload.HorizontalMeasurementTypesPayload.Error);
            Assert.AreEqual(_measurementTypes, payload.HorizontalMeasurementTypesPayload.MeasurementTypes);
            Assert.AreEqual(2, payload.HorizontalMeasurementTypesPayload.SelectedTypes.Count());
            Assert.AreEqual(_weight, payload.HorizontalMeasurementTypesPayload.SelectedTypes.First());
            Assert.AreEqual(_bodyFat, payload.HorizontalMeasurementTypesPayload.SelectedTypes.Skip(1).First());
        }

        [Test]
        public void Test_Index_Post_SuccessfulSave()
        {
            // Arrange
            InitialiseControllerWithMockAccountProvider();
            SetupLookupStubs();
            SetupMembershipServiceAccountStub();
            _accountProvider.Expect(ap => ap.UpdateAccount(null, DateTime.Now, null, null, null)).IgnoreArguments();
            FormCollection postValues = new FormCollection()
                                            {
                                                { "Email", AccountEmail },
                                                { _weight.ToFormName(), "true" },
                                                { "UnitForLengthId", "0" },
                                                { "UnitForWeightId", "0" },
                                                { "DateOfBirth", "01-01-1990"}
                                            };

            // Act
            ViewResult result = (ViewResult)_controller.Index(postValues);

            // Assert
            Assert_IndexViewData(result);
            var payload = (IndexPayload)result.ViewData.Model;
            Assert.AreEqual("Your account has been updated", payload.Success);
            Assert.IsNull(payload.HorizontalMeasurementTypesPayload.Error);
            Assert.AreEqual(_measurementTypes, payload.HorizontalMeasurementTypesPayload.MeasurementTypes);
            Assert.AreEqual(1, payload.HorizontalMeasurementTypesPayload.SelectedTypes.Count());
            Assert.AreEqual(_weight, payload.HorizontalMeasurementTypesPayload.SelectedTypes.First());

            _accountProvider.VerifyAllExpectations();
        }

        [Test]
        public void Test_Index_Post_SuccessfulSaveWithTwoIndicators()
        {
            // Arrange
            InitialiseControllerWithMockAccountProvider();
            SetupLookupStubs();
            SetupMembershipServiceAccountStub();
            _accountProvider.Expect(ap => ap.UpdateAccount(null, DateTime.Now, null, null, null)).IgnoreArguments();
            FormCollection postValues = new FormCollection()
                                            {
                                                { "Email", AccountEmail },
                                                { _weight.ToFormName(), "true" },
                                                { _bodyFat.ToFormName(), "true" },
                                                { "UnitForLengthId", "0" },
                                                { "UnitForWeightId", "0" },
                                                { "DateOfBirth", "01-01-1990"}
                                            };

            // Act
            ViewResult result = (ViewResult)_controller.Index(postValues);

            // Assert
            Assert_IndexViewData(result);
            var payload = (IndexPayload)result.ViewData.Model;
            Assert.AreEqual("Your account has been updated", payload.Success);
            Assert.IsNull(payload.HorizontalMeasurementTypesPayload.Error);
            Assert.AreEqual(_measurementTypes, payload.HorizontalMeasurementTypesPayload.MeasurementTypes);
            Assert.AreEqual(2, payload.HorizontalMeasurementTypesPayload.SelectedTypes.Count());
            Assert.AreEqual(_weight, payload.HorizontalMeasurementTypesPayload.SelectedTypes.First());
            Assert.AreEqual(_bodyFat, payload.HorizontalMeasurementTypesPayload.SelectedTypes.Skip(1).First());
            _accountProvider.VerifyAllExpectations();
        }

        [Test]
        public void Test_Index_Post_ThreeIndicatorsGiveError()
        {
            // Arrange
            InitialiseControllerWithMockAccountProvider();
            SetupLookupStubs();
            SetupMembershipServiceAccountStub();
            _accountProvider.AssertWasNotCalled(ap => ap.UpdateAccount(Arg<Account>.Is.Anything,
                Arg<DateTime>.Is.Anything,
                Arg<UnitOfLength>.Is.Anything,
                Arg<UnitOfWeight>.Is.Anything, Arg<IEnumerable<MeasurementType>>.Is.Anything));
            FormCollection postValues = new FormCollection()
                                            {
                                                { "Email", AccountEmail },
                                                { _weight.ToFormName(), "true" },
                                                { _bodyFat.ToFormName(), "true" },
                                                { _chest.ToFormName(), "true" },
                                                { "UnitForLengthId", "0" },
                                                { "UnitForWeightId", "0" },
                                                { "DateOfBirth", "01-01-1990"}
                                            };

            // Act
            ViewResult result = (ViewResult)_controller.Index(postValues);

            // Assert
            Assert_IndexViewData(result);

            var payload = (IndexPayload)result.ViewData.Model;
            Assert.AreEqual("You can select upto two items", payload.HorizontalMeasurementTypesPayload.Error);
            Assert.IsNull(payload.Success);
            Assert.AreEqual(_measurementTypes, payload.HorizontalMeasurementTypesPayload.MeasurementTypes);
            Assert.AreEqual(3, payload.HorizontalMeasurementTypesPayload.SelectedTypes.Count());
            Assert.AreEqual(_weight, payload.HorizontalMeasurementTypesPayload.SelectedTypes.First());
            Assert.AreEqual(_bodyFat, payload.HorizontalMeasurementTypesPayload.SelectedTypes.Skip(1).First());
            Assert.AreEqual(_chest, payload.HorizontalMeasurementTypesPayload.SelectedTypes.Skip(2).First());
            _accountProvider.VerifyAllExpectations();
        }

        [Test]
        public void Test_Index_Post_ModelExceptionAddedToErrors()
        {
            const string emailErrorMessage = "Crazy Email Address";
            const string emailErrorKey = "email";

            // Arrange
            InitialiseControllerWithMockAccountProvider();
            SetupLookupStubs();
            SetupMembershipServiceAccountStub();
            _accountProvider.Expect(ap => ap.UpdateAccount(null, DateTime.Now, null, null, null))
                .Throw(new ValidationException(new NameValueCollection() { { emailErrorKey, emailErrorMessage}}))
                .IgnoreArguments();
            FormCollection postValues = new FormCollection()
                                            {
                                                { "Email", "CrazyEmail" },
                                                { _weight.ToFormName(), "true" },
                                                { "UnitForLengthId", "0" },
                                                { "UnitForWeightId", "0" },
                                                { "DateOfBirth", "01-01-1990"}
                                            };

            // Act
            ViewResult result = (ViewResult)_controller.Index(postValues);

            // Assert
            Assert_IndexViewData(result);
            var payload = (IndexPayload)result.ViewData.Model;
            Assert.IsNull(payload.Success);
            Assert.IsNull(payload.HorizontalMeasurementTypesPayload.Error);
            Assert.AreEqual(_measurementTypes, payload.HorizontalMeasurementTypesPayload.MeasurementTypes);
            Assert.AreEqual(1, payload.HorizontalMeasurementTypesPayload.SelectedTypes.Count());
            Assert.AreEqual(_weight, payload.HorizontalMeasurementTypesPayload.SelectedTypes.First());
            Assert.AreEqual(emailErrorMessage, result.ViewData.ModelState[emailErrorKey].Errors[0].ErrorMessage);
            _accountProvider.VerifyAllExpectations();
        }

        #endregion

        #region Terms and conditions and privacy

        [Test]
        public void Test_TermsAndConditions_Get_ReturnsView()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();

            // Act
            ViewResult result = (ViewResult)_controller.TermsAndConditions();

            // Assert
            // Nothing to assert
        }

        [Test]
        public void Test_Privacy_Get_ReturnsView()
        {
            // Arrange
            InitialiseControllerWithMockMembershipService();

            // Act
            ViewResult result = (ViewResult)_controller.Privacy();

            // Assert
            // Nothing to assert
        }

        #endregion

        #region Logon

        [Test]
        public void Test_Logon_Get_ReturnsView()
        {
            // Arrange
            InitialiseControllerWithMockFormsAuthenticator();

            // Act
            ViewResult result = (ViewResult) _controller.LogOn();

            // Assert
            // Nothing to assert
        }

        [Test]
        public void Test_Logon_Post_RedirectsToHomepage()
        {
            // Arrange
            InitialiseControllerWithMockFormsAuthenticator();
            _membershipService.Stub(ms => ms.ValidateUser(null, null)).Return(true).IgnoreArguments();
            _formsAuth.Expect(fa => fa.SignIn(null, true)).IgnoreArguments();

            // Act
            RedirectResult result = (RedirectResult) _controller.LogOn("testuser", "password", true, null);

            // Assert
            Assert.AreEqual(MockHttpContext.Url, result.Url);
            _formsAuth.VerifyAllExpectations();
        }

        [Test]
        public void Test_Logon_Post_RedirectsToGivenUrl()
        {
            // Arrange
            const string dummyUrl = "http://someurl.com";
            InitialiseControllerWithMockFormsAuthenticator();
            _membershipService.Stub(ms => ms.ValidateUser(null, null)).Return(true).IgnoreArguments();
            _formsAuth.Expect(fa => fa.SignIn(null, true)).IgnoreArguments();

            // Act
            RedirectResult result = (RedirectResult)_controller.LogOn("testuser", "password", true, dummyUrl);

            // Assert
            Assert.AreEqual(dummyUrl, result.Url);
            _formsAuth.VerifyAllExpectations();
        }

        [Test]
        public void Test_Logon_Post_WrongUsernamePassword()
        {
            // Arrange
            InitialiseControllerWithMockFormsAuthenticator();
            NameValueCollection errors = new NameValueCollection();
            errors.Add("_FORM", "The username or password provided is incorrect.");
            _accountProvider.Stub(ap => ap.ValidateLogOn(null, null, _membershipService))
                .IgnoreArguments().Throw(new ValidationException(errors));
            _membershipService.Stub(ms => ms.ValidateUser(null, null)).Return(false).IgnoreArguments();
            _formsAuth.AssertWasNotCalled(fa => fa.SignIn(Arg<string>.Is.Anything, Arg<bool>.Is.Anything));

            // Act
            ViewResult result = (ViewResult)_controller.LogOn("testuser", "password", true, null);

            // Assert
            Assert.AreEqual("The username or password provided is incorrect.", result.ViewData.ModelState["_FORM"].Errors[0].ErrorMessage);
            _formsAuth.VerifyAllExpectations();
        }

        [Test]
        public void Test_Logon_Post_EmptyUsername()
        {
            // Arrange
            InitialiseControllerWithMockFormsAuthenticator();
            NameValueCollection errors = new NameValueCollection();
            errors.Add("username", "You must specify a username.");
            _accountProvider.Stub(ap => ap.ValidateLogOn(null, null, _membershipService))
                .IgnoreArguments().Throw(new ValidationException(errors));
            _membershipService.Stub(ms => ms.ValidateUser(null, null)).Return(false).IgnoreArguments();
            _formsAuth.AssertWasNotCalled(fa => fa.SignIn(Arg<string>.Is.Anything, Arg<bool>.Is.Anything));

            // Act
            ViewResult result = (ViewResult)_controller.LogOn("", "password", true, null);

            // Assert
            Assert.AreEqual("You must specify a username.", result.ViewData.ModelState["username"].Errors[0].ErrorMessage);
            _formsAuth.VerifyAllExpectations();
        }

        [Test]
        public void Test_Logon_Post_EmptyPassword()
        {
            // Arrange
            InitialiseControllerWithMockFormsAuthenticator();
            NameValueCollection errors = new NameValueCollection();
            errors.Add("password", "You must specify a password.");
            _accountProvider.Stub(ap => ap.ValidateLogOn(null, null, _membershipService))
                .IgnoreArguments().Throw(new ValidationException(errors));
            _membershipService.Stub(ms => ms.ValidateUser(null, null)).Return(false).IgnoreArguments();
            _formsAuth.AssertWasNotCalled(fa => fa.SignIn(Arg<string>.Is.Anything, Arg<bool>.Is.Anything));

            // Act
            ViewResult result = (ViewResult)_controller.LogOn("testuser", "", true, null);

            // Assert
            Assert.AreEqual("You must specify a password.", result.ViewData.ModelState["password"].Errors[0].ErrorMessage);
            _formsAuth.VerifyAllExpectations();
        }

        #endregion

        #region Logoff

        [Test]
        public void Test_Logoff_Get_RedirectsToHomepage()
        {
            // Arrange
            InitialiseControllerWithMockFormsAuthenticator();
            _formsAuth.Expect(fa => fa.SignOut());

            // Act
            RedirectToRouteResult result = (RedirectToRouteResult) _controller.LogOff();

            // Assert
            Assert.AreEqual("Index", result.RouteValues["action"]);
            Assert.AreEqual("Home", result.RouteValues["controller"]);
            _formsAuth.VerifyAllExpectations();
        }

        #endregion
    }
}